<?php


namespace Aoe\Util\Bus\Config;

use Aoe\Util\Bus\Accessor;
use Throwable;
use function array_forget;
use function array_get;
use function array_has;
use function array_set;

/**
 * # 配置类类
 *    * 级联查询，修改本级
 *    * 通过异步器读取数据
 */
class Config
{
    /**
     * @var array 配置内容
     */
    protected array $items = [];
    
    /**
     * @var bool 已修改
     */
    protected bool $modified = false;
    
    /**
     * @var ?Accessor 存取器
     */
    protected ?Accessor $accessor;
    
    /**
     * @param Accessor|array $accessor 加载器|数据
     * @param ?Config        $upper 上级配置器
     */
    public function __construct(Accessor | array $accessor = [], protected ?Config $upper = null)
    {
        if ($accessor instanceof Accessor) {
            $this->accessor = $accessor;
            $this->load();
        } else {
            $this->items = $accessor;
        }
    }
    
    public function load(): void
    {
        if (!$this->accessor) return;
        
        try {
            $values = $this->accessor->load();
        } catch (Throwable) {
            return;
        }

        if (!$values) return;
        
        $this->items = $values;
        $this->modified = false;
    }
    
    /**
     * ## 工具函数：生成配置文件文件名
     *
     * @param string $dir
     * @param string $file
     * @param string $ext
     *
     * @return string
     */
    public static function file(string $dir, string $file = 'config', string $ext = '.php'): string
    {
        return safe_file($dir, strtolower($file), $ext);
    }
    
    public function all(): array
    {
        return $this->items;
    }
    
    public function get(string $key, $default = null, bool $withUpper = true): mixed
    {
        if (!$withUpper or !$this->upper)
            return $this->self_has($key) ? $this->self_get($key) : $default;
        
        if (!$this->has($key, false)) return $this->upper->get($key, $default);
        
        $self = $this->self_get($key);
        
        if (!is_array($self)) return $self;
        
        return array_merge_recursive($this->upper->get($key, []), $self);
    }
    
    protected function self_has(string $key): bool
    {
        return array_has($this->items, $key);
    }
    
    protected function self_get($key): mixed
    {
        return array_get($this->items, $key);
    }
    
    public function has(string $key, bool $withUpper = true): bool
    {
        return $this->self_has($key) || ($withUpper && $this->upper?->has($key));
    }
    
    public function set($value, string | array $key): void
    {
        if (is_array($key)) {
            foreach ($key as $k => $v) $this->set($v, $k);
            return;
        }
        
        if ($value === null) {
            $this->modified = array_forget($this->items, $key);
            return;
        }
        
        $this->modified = array_set($this->items, $key, $value);
    }
    
    public function save(?array $values = null): void
    {
        if ($values) $this->items = $values;
        
        try {
            $this->accessor?->save($this->items);
        } catch (Throwable) {}
        
        $this->modified = false;
    }
    
    /**
     * ## 续接配置
     *
     * @param Accessor|array $async
     *
     * @return Config
     */
    public function branch(Accessor | array $async): Config
    {
        return new static($async, $this);
    }
    
    /**
     * 插入配置
     *
     * @param Accessor|array $async
     *
     * @return void
     * @noinspection PhpUnused
     */
    public function before(Accessor | array $async): void
    {
        $this->upper = new static($async, $this->upper);
    }
}
